/*
 * Copyright (C) 2006 The Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.google.blockly.ohos.detector;

import ohos.agp.components.VelocityDetector;
import ohos.multimodalinput.event.TouchEvent;
import ohos.utils.PlainArray;

public class VelocityTracker {
    PlainArray<VelocityDetector> mMap = new PlainArray<>();
    private int mActivePointerId = 0;
    private boolean needScale = false;
    private float mMaxVelocity = Float.MAX_VALUE;

    public VelocityTracker() {
        mMap.put(0, obtainVelocityDetector());
    }

    public void addMovement(TouchEvent ev) {
        int index = ev.getIndex();
        int id = ev.getPointerId(index);
        VelocityDetector detector = mMap.get(id, null);
        if (detector == null) {
            detector = obtainVelocityDetector();
            mMap.put(id, detector);
        }
        addMovement(detector, ev);
        switch (ev.getAction()) {
            case TouchEvent.PRIMARY_POINT_DOWN:
                mActivePointerId = id;
                break;
            case TouchEvent.OTHER_POINT_UP:
                if (id == mActivePointerId) {
                    mActivePointerId = (id == 0) ? 1 : 0;
                }
                break;
            default:
                break;
        }
    }

    public void calculateCurrentVelocity(int units, float maxVxVelocity, float maxVyVelocity) {
        needScale = false;
        mMaxVelocity = Float.MAX_VALUE;
        for (int i = 0; i < mMap.size(); i++) {
            VelocityDetector vd = mMap.valueAt(i);
            if (vd != null) {
                vd.calculateCurrentVelocity(units, maxVxVelocity, maxVyVelocity);
            }
        }
    }

    public void calculateCurrentVelocity(int units, float maxVelocity) {
        needScale = true;
        mMaxVelocity = maxVelocity;
        for (int i = 0; i < mMap.size(); i++) {
            VelocityDetector vd = mMap.valueAt(i);
            if (vd != null) {
                vd.calculateCurrentVelocity(units);
            }
        }
    }

    public void calculateCurrentVelocity(int units) {
        needScale = false;
        mMaxVelocity = Float.MAX_VALUE;
        for (int i = 0; i < mMap.size(); i++) {
            VelocityDetector vd = mMap.valueAt(i);
            if (vd != null) {
                vd.calculateCurrentVelocity(units);
            }
        }
    }

    public float getXVelocity() {
        return getXVelocity(mActivePointerId);
    }

    public float getYVelocity() {
        return getYVelocity(mActivePointerId);
    }

    public float getXVelocity(int id) {
        VelocityDetector vd = mMap.get(id, null);
        if (vd == null) {
            return 0;
        }
        return getXVelocityWithScale(vd);
    }

    public float getYVelocity(int id) {
        VelocityDetector vd = mMap.get(id, null);
        if (vd == null) {
            return 0;
        }
        return getYVelocityWithScale(vd);
    }

    public void clear() {
        needScale = false;
        mMaxVelocity = Float.MAX_VALUE;
        for (int i = 0; i < mMap.size(); i++) {
            VelocityDetector vd = mMap.valueAt(i);
            if (vd != null) {
                velocityclear(vd);
            }
        }
    }

    public void recycle() {
        clear();
        mMap.clear();
    }

    private VelocityDetector obtainVelocityDetector() {
        return VelocityDetector.obtainInstance();
    }

    private void addMovement(VelocityDetector velocityDetector, TouchEvent touchEvent) {
        velocityDetector.addEvent(touchEvent);
    }

    private void velocityclear(VelocityDetector velocityDetector) {
        velocityDetector.clear();
    }

    private float getXVelocityWithScale(VelocityDetector velocityDetector) {
        if (!needScale) {
            return getXVelocity(velocityDetector);
        } else {
            float xVelocity = getXVelocity(velocityDetector);
            float yVelocity = getYVelocity(velocityDetector);
            if (xVelocity * xVelocity + yVelocity * yVelocity > mMaxVelocity * mMaxVelocity) {
                xVelocity /= Math.sqrt((xVelocity * xVelocity + yVelocity * yVelocity) / (mMaxVelocity * mMaxVelocity));
            }
            return xVelocity;
        }
    }

    private float getYVelocityWithScale(VelocityDetector velocityDetector) {
        if (!needScale) {
            return getYVelocity(velocityDetector);
        } else {
            float xVelocity = getXVelocity(velocityDetector);
            float yVelocity = getYVelocity(velocityDetector);
            if (xVelocity * xVelocity + yVelocity * yVelocity > mMaxVelocity * mMaxVelocity) {
                yVelocity /= Math.sqrt((xVelocity * xVelocity + yVelocity * yVelocity) / (mMaxVelocity * mMaxVelocity));
            }
            return yVelocity;
        }
    }

    private float getXVelocity(VelocityDetector velocityDetector) {
        return velocityDetector.getHorizontalVelocity();
    }

    private float getYVelocity(VelocityDetector velocityDetector) {
       return velocityDetector.getVerticalVelocity();
    }
}
